﻿#define _CRT_SECURE_NO_WARNINGS
#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_cInst;

Span* CentralCache::GetOneSpan(SpanList& spanList, size_t size)
{
	assert(size <= MAX_BYTES);

	Span* it = spanList.Begin();
	while (it != spanList.End())
	{
		if (it->_freeList != nullptr)
			return it;
		it = it->_next;
	}
	spanList._mtx.unlock();

	//SpanLists对应的桶里面没有Span , 想PageCache申请一批Span
	PageCache::GetInstance()->_mtx.lock();

	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
	span->_isUse = true;
	span->_objSize = size;

	PageCache::GetInstance()->_mtx.unlock();
	
	//对Span 进行切分size大小的内存对象 链入自由链表
		//页号 = 页起始地址 >> PAGE_SHIFT
		//页起始地址 = 页号 << PAGE_SHIFT
	char* start = (char*)(span->_pageId << PAGE_SHIFT);
	size_t bytes = span->_n << PAGE_SHIFT;
	char* end = start + bytes;

	span->_freeList = start;
	void* tail = span->_freeList;
	start += size;

	while (start < end)
	{
		NextObj(tail) = start;
		tail = NextObj(tail);
		start += size;
	}
	NextObj(tail) = nullptr;


	spanList._mtx.lock();
	spanList.PushFront(span);

	return span;
}

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	assert(size <= MAX_BYTES);

	size_t index = SizeClass::Index(size);

	_spanLists[index]._mtx.lock();

	Span* span = GetOneSpan(_spanLists[index] , size);

	size_t actualNum = 1;
	size_t i = 0;
	start = span->_freeList;
	end = start;

	while (i < batchNum - 1 && NextObj(end) != nullptr)
	{
		++i;
		++actualNum;
		end = NextObj(end);
	}

	span->_useCount += actualNum;
	span->_freeList = NextObj(end);

	_spanLists[index]._mtx.unlock();

	return actualNum;
}

void CentralCache::ReleaseListToSpan(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();
	while (start)
	{
		void* next = NextObj(start);

		Span* span = PageCache::GetInstance()->MapIdToSpan(start);

		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;
		
		if (span->_useCount == 0)
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_prev = nullptr;
			span->_next = nullptr;
			_spanLists[index]._mtx.unlock();

			PageCache::GetInstance()->_mtx.lock();
			PageCache::GetInstance()->RealseSpanToPageCache(span);
			PageCache::GetInstance()->_mtx.unlock();

			_spanLists[index]._mtx.lock();
		}

		start = next;
	}
	_spanLists[index]._mtx.unlock();
}

